﻿using System;
using System.IO;
using System.Reflection;
using System.Windows;
using System.Windows.Browser;
using System.Windows.Resources;

namespace FirstFloor.P2P
{
    /// <summary>
    /// Provides interoperability with the Live Messenger P2P stack. This class is for internal use only.
    /// </summary>
    public class P2PInterop
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="P2PInterop"/> class.
        /// </summary>
        internal P2PInterop()
        {
            // register myself as scriptable
            HtmlPage.RegisterScriptableObject("P2PInterop", this);

            // inject P2P JScript
            Assembly assembly = Assembly.GetExecutingAssembly();
            using (Stream stream = assembly.GetManifestResourceStream("FirstFloor.P2P.Resources.P2P.js")) {
                using (StreamReader reader = new StreamReader(stream)) {
                    string jscript = reader.ReadToEnd();

                    HtmlPage.Window.Eval(jscript);
                }
            }

            // register this application
            HtmlPage.Window.Invoke("p2pRegister", HtmlPage.Plugin);
        }

        private void ValidateState()
        {
            if (P2PApplication.Current.State != P2PApplicationState.Connected) {
                throw new InvalidOperationException(string.Format("Invalid application state {0}", P2PApplication.Current.State));
            }
        }

        /// <summary>
        /// Adds a user.
        /// </summary>
        /// <param name="email">The email.</param>
        /// <param name="globalIp">The global ip.</param>
        /// <param name="localIp">The local ip.</param>
        /// <param name="name">The name.</param>
        /// <param name="puid">The puid.</param>
        /// <param name="isMe">if set to <c>true</c> is me.</param>
        /// <param name="isInviter">if set to <c>true</c> is inviter.</param>
        [ScriptableMember]
        public void AddUser(string email, string globalIp, string localIp, string name, string puid, bool isMe, bool isInviter)
        {
            P2PUser user = new P2PUser(email, globalIp, localIp, name, puid, isMe, isInviter);
            P2PApplication.Current.Users.Add(user);
        }

        /// <summary>
        /// Handles the OnAppClose channel event.
        /// </summary>
        [ScriptableMember]
        public void Channel_OnAppClose()
        {
            P2PApplication.Current.OnShutdown();
        }

        /// <summary>
        /// Handles the OnDataError channel event.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <param name="data">The data.</param>
        [ScriptableMember]
        public void Channel_OnDataError(int type, string data)
        {
            P2PApplication.Current.OnSendDataFailed(type, data);
        }

        /// <summary>
        /// Handles the OnDataReceived channel event.
        /// </summary>
        /// <param name="data">The data.</param>
        [ScriptableMember]
        public void Channel_OnDataReceived(string data)
        {
            P2PApplication.Current.OnDataReceived(data);
        }

        /// <summary>
        /// Handles the OnIMReceived channel event.
        /// </summary>
        /// <param name="im">The im.</param>
        [ScriptableMember]
        public string Channel_OnIMReceived(string im)
        {
            return P2PApplication.Current.OnIMReceived(im);
        }

        /// <summary>
        /// Handles the OnRemoteAppClosed channel event.
        /// </summary>
        [ScriptableMember]
        public void Channel_OnRemoteAppClosed()
        {
            P2PApplication.Current.OnClose(P2PApplicationCloseReason.ClosedByRemoteApplication);
        }

        /// <summary>
        /// Handles the OnRemoteAppLoaded channel event.
        /// </summary>
        [ScriptableMember]
        public void Channel_OnRemoteAppLoaded()
        {
            P2PApplication.Current.OnConnect();
        }

        /// <summary>
        /// Handles the OnTypeChanged channel event.
        /// </summary>
        /// <param name="type">The type.</param>
        [ScriptableMember]
        public void Channel_OnTypeChanged(P2PConnectionType type)
        {
            P2PApplication.Current.OnConnectionTypeChanged(type);
        }

        internal bool P2PNetworkAvailable
        {
            get { return (bool)HtmlPage.Window.Invoke("p2pNetworkAvailable"); }
        }

        internal void Connect()
        {
            try {
                HtmlPage.Window.Invoke("p2pConnect");
            }
            catch (Exception e) {
                throw new P2PInteropException("Failed to connect", e);
            }
        }

        internal void Close()
        {
            try {
                HtmlPage.Window.Invoke("p2pClose");
            }
            catch (Exception e) {
                throw new P2PInteropException("Failed to close", e);
            }
        }

        internal void EnterIM(string im)
        {
            ValidateState();
            try {
                HtmlPage.Window.Invoke("p2pEnterIM", im);
            }
            catch (Exception e) {
                throw new P2PInteropException("Failed to enter instant message", e);
            }
        }

        internal void SendData(string data)
        {
            ValidateState();
            try {
                HtmlPage.Window.Invoke("p2pSendData", data);
            }
            catch (Exception e) {
                throw new P2PInteropException("Failed to send data", e);
            }
        }

        internal void SendIM(string im)
        {
            ValidateState();
            try {
                HtmlPage.Window.Invoke("p2pSendIM", im);
            }
            catch (Exception e) {
                throw new P2PInteropException("Failed to send instant message", e);
            }
        }

        internal void SendIMAsUser(string im)
        {
            ValidateState();
            try {
                HtmlPage.Window.Invoke("p2pSendIMAsUser", im);
            }
            catch (Exception e) {
                throw new P2PInteropException("Failed to send instant message as user", e);
            }
        }
    }
}
